const radix = 12;
const base = 128 - radix;

function crypto1(value: number){
    value -= base;
    let h = Math.floor(value / radix) + base;
    let l = value % radix + base;
    return String.fromCharCode(h) + String.fromCharCode(l);
}

const encodermap = <any>{};
const decodermap = <any>{};

for(let i = 0; i < 256; i++){
    let code = '';
    let v = i + 1;
    if(v >= base){
        code = crypto1(v);
    }
    else{
        code = String.fromCharCode(v);
    }
    encodermap[i] = code;
    decodermap[code] = i;
}

/**MD5加密算法 */
function encode(data){
    let content = '';
    let len = data.length;
    let a = (len >> 24) & 0xff;
    let b = (len >> 16) & 0xff;
    let c = (len >> 8) & 0xff;
    let d = len & 0xff;
    content += encodermap[a];
    content += encodermap[b];
    content += encodermap[c];
    content += encodermap[d];
    for(let i = 0; i < data.length; i++){
        content += encodermap[data[i]];
    }
    return content;
}

function getCode(content: string, index: number){
    let c = <string|number>content.charCodeAt(index);
    if(c >= base){
        c = content.charAt(index) + content.charAt(index + 1);
    }
    else{
        c = content.charAt(index);
    }
    return c;
}
/**MD5解密算法 */
function decode(content){
    let index = 0;
    let len = 0;
    for(let i = 0; i < 4; i++){
        let c = getCode(content, index);
        index += c.length;
        let v = decodermap[c];
        len |= v << (3 - i) * 8;
    }

    let newData = new Uint8Array(len);
    let cnt = 0;
    while(index < content.length){
        let c = getCode(content, index);
        index += c.length;
        newData[cnt] = decodermap[c];
        cnt++;
    }
    return newData;
}

abstract class VoiceMgr1 {
    onPlayCallback;
    _voiceMediaPath: string;
    abstract init(): void;
    abstract clearCache(filename: string): void;
    abstract prepare(filename: string): void;
    abstract release(): void;
    abstract cancel(): void;
    abstract writeVoice(filename: string, voiceData: string): void;
    abstract play(filename: string): void;
    abstract stop(): void;
    abstract getVoiceLevel(maxLevel: number): number;
    abstract getVoiceData(filename: string): string;
    abstract setStorageDir(dir: string): void;


}

export class VoiceMgr extends VoiceMgr1{
    onPlayCallback;
    /**录音文件路径 */
    _voiceMediaPath: string;

    /**安卓函数命名空间跟路径 */
    ANDROID_ROOT_PATH = 'com/babykylin/';
    
    init() {
        if(cc.sys.isNative){
            this._voiceMediaPath = jsb.fileUtils.getWritablePath() + '/voicemsgs/';
            this.setStorageDir(this._voiceMediaPath);
        }
    }

    /**
     * 调用原声系统API清除音频文件
     */
    clearCache(filename: string) {
        if(cc.sys.isNative){
            const url = this._voiceMediaPath + filename;
            if(jsb.fileUtils.isFileExist(url)){
                jsb.fileUtils.removeFile(url);
            }
            if(jsb.fileUtils.isFileExist(url + '.wav')){
                jsb.fileUtils.removeFile(url + '.wav');
            }
        }
    }

    /**
     * 调用原生系统API开始录音
     * @param filename 
     */
    prepare(filename: string) {
        if(!cc.sys.isNative){
            return;
        }
        cc.vv.audioMgr.pauseAll();
        this.clearCache(filename);
        if(cc.sys.os == cc.sys.OS_ANDROID){
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoiceRecorder', 'prepare', '(Ljava/lang/String;)V', filename);
        }
        else if(cc.sys.os == cc.sys.OS_IOS){
            jsb.reflection.callStaticMethod('VoiceSDK', 'prepareRecord:', filename);
        }

    }

    /**录音完成后释放资源 */
    release() {
        if (!cc.sys.isNative) {
            return;
        }
        cc.vv.audioMgr.resumeAll();
        if (cc.sys.os == cc.sys.OS_ANDROID) {
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoiceRecorder', 'release', '()V');
        }
        else if (cc.sys.os == cc.sys.OS_IOS) {
            jsb.reflection.callStaticMethod("VoiceSDK", "finishRecord");
        }
    }

    /**取消录音 */
    cancel() {
        if (!cc.sys.isNative) {
            return;
        }
        cc.vv.audioMgr.resumeAll();
        if (cc.sys.os == cc.sys.OS_ANDROID) {
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoiceRecorder', 'cancel', '()V');
        }
        else if (cc.sys.os == cc.sys.OS_IOS) {
            jsb.reflection.callStaticMethod("VoiceSDK", "cancelRecord");
        }
    }
    /**写入录音文件数据 */
    writeVoice(filename: string, voiceData: string) {
        if(!cc.sys.isNative){
            return;
        }
        if(voiceData && voiceData.length > 0){
            const fileData = decode(voiceData);
            const url = this._voiceMediaPath + filename;
            this.clearCache(filename);
            jsb.fileUtils.writeDataToFile(fileData, url);
        }
    }
    /**播放音频文件 */
    play(filename: string) {
        if(!cc.sys.isNative){
            return;
        }

        cc.vv.audioMgr.pauseAll();
        if(cc.sys.os == cc.sys.OS_ANDROID){
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoicePlayer', 'play', '(Ljava/lang/String;)V', filename);
        }
        else if(cc.sys.os == cc.sys.OS_IOS){
            jsb.reflection.callStaticMethod('VoiceSDK', 'play:', filename);
        }
    }
    /**停止播放 */
    stop() {
        if(!cc.sys.isNative){
            return;
        }

        cc.vv.audioMgr.resumeAll();
        if(cc.sys.os == cc.sys.OS_ANDROID){
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoicePlayer', 'stop', '()V');
        }
        else if(cc.sys.os == cc.sys.OS_IOS){
            jsb.reflection.callStaticMethod('VoiceSDK', 'stopPlay');
        }
    }

    /**获取录音文件的音量 */
    getVoiceLevel(maxLevel: number) {
        if(cc.sys.os == cc.sys.OS_ANDROID){
            return jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoiceRecorder', 'getVoiceLevel', '(I)I', maxLevel);
        }
        else if(cc.sys.os == cc.sys.OS_IOS){
            // IOS里没写获取音量的方法，以后补上
            return Math.floor(Math.random() * maxLevel + 1);
        }
    }

    /**获取录音文件的原始二进制信息，然后用MD5加密后返回 */
    getVoiceData(filename: string) {
        if(cc.sys.isNative){
            let url = this._voiceMediaPath + filename;
            let fileData = jsb.fileUtils.getDataFromFile(url);
            if(fileData){
                let content = encode(fileData);
                return content;
            }
        }
        return '';
    }

    /**创建存储目录 */
    setStorageDir(dir: string) {
        if(!cc.sys.isNative){
            return;
        }
        if(cc.sys.os == cc.sys.OS_ANDROID){
            jsb.reflection.callStaticMethod(this.ANDROID_ROOT_PATH + 'VoiceRecorder', 'setStorageDir', '(Ljava/lang/String;)V', dir);
        }
        else if(cc.sys.os == cc.sys.OS_IOS){
            jsb.reflection.callStaticMethod('VoiceSDK', 'setStorageDir:', dir);
            if(!jsb.fileUtils.isDirectoryExist(dir)){
                jsb.fileUtils.createDirectory(dir);
            }
        }
    }
}